package Main;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;

import Maze.HorizontalTile;
import Maze.Maze;
import Maze.Mouse;
import Maze.VerticalTile;
import SearchAlgorithms.GeneralSearch;
import Tree.Node;

public class MouseInAMaze {

	/**
	 * It initializes the problem and the general search and returns what the
	 * search returns
	 * 
	 * @param strategy
	 *            is the strategy of the algorithm the user specifies
	 * @param visualize
	 *            is whether or not to print on the console
	 * @return the path from the initial state to the goal
	 * 
	 * 
	 */
	public static LinkedList<Object> MiM(String strategy, boolean visualize) {

		Maze maze = drawMaze();
		Operators operators = new OperatorsMouseInAMaze();
		GoalTest goalTest = new GoalTestMouseInAMaze();
		GenericSearchProblem problem = new GenericSearchProblem(maze,
				operators, goalTest);
		GeneralSearch generalSearch = new GeneralSearch(problem, strategy,
				visualize);
		return generalSearch.search();
	}

	/**
	 * This method draws the initial maze which the mouse in row 2 position 0 &
	 * 1 and with random number of vertical and horizontal tiles spread in
	 * random locations throughout the maze
	 * 
	 * @return The initial Maze
	 * 
	 * 
	 */

	public static Maze drawMaze() {

		Random randomGenerator = new Random();
		Maze maze = new Maze();
		// V tiles num
		int v = randomGenerator.nextInt(15);
		ArrayList<VerticalTile> verticalTiles = new ArrayList<VerticalTile>();
		// H tiles num
		int h = randomGenerator.nextInt(15);
		ArrayList<HorizontalTile> horizontalTiles = new ArrayList<HorizontalTile>();

		String[][] grid = new String[6][6];

		Mouse mouse = new Mouse();

		mouse.setStartX(0);
		mouse.setEndX(1);
		mouse.setStartY(2);
		mouse.setEndY(2);
		mouse.setName("M ");

		grid[0][2] = mouse.getName();
		grid[1][2] = mouse.getName();

		int vCounter = 0;
		int hCounter = 0;

		for (int i = 0; i < 6; i++) {
			for (int j = 0; j < 6; j++) {

				int tile = randomGenerator.nextInt(3);

				if (tile == 0) {
					if (vCounter < v) {

						VerticalTile vt = new VerticalTile();
						int tileLength = randomGenerator.nextInt(2);

						if (tileLength == 0) {
							vt.setLength(2);
						} else {
							vt.setLength(3);
						}

						if (j + 1 < 6) {
							if ((grid[i][j] == null)
									&& (grid[i][j + 1] == null)) {
								if (vt.getLength() == 3 && (j + 2 < 6)) {
									if (grid[i][j + 2] == null) {

										grid[i][j] = "V" + (vCounter + 1);
										grid[i][j + 1] = "V" + (vCounter + 1);
										grid[i][j + 2] = "V" + (vCounter + 1);

										vt.setStartX(i);
										vt.setEndX(i);
										vt.setStartY(j);
										vt.setEndY(j + 2);
										vt.setName("V" + (vCounter + 1));

										verticalTiles.add(vt);

										vCounter++;

									}
								} else if (vt.getLength() == 2) {

									grid[i][j] = "V" + (vCounter + 1);
									grid[i][j + 1] = "V" + (vCounter + 1);

									vt.setStartX(i);
									vt.setEndX(i);
									vt.setStartY(j);
									vt.setEndY(j + 1);
									vt.setName("V" + (vCounter + 1));

									verticalTiles.add(vt);

									vCounter++;
								}
							}
						}
					}

				} else if (tile == 1) {

					if (hCounter < h) {

						HorizontalTile ht = new HorizontalTile();

						if (i + 1 < 6) {
							if ((grid[i][j] == null)
									&& (grid[i + 1][j] == null)) {

								grid[i][j] = "H" + (hCounter + 1);
								grid[i + 1][j] = "H" + (hCounter + 1);

								ht.setStartX(i);
								ht.setEndX(i + 1);
								ht.setStartY(j);
								ht.setEndY(j);
								ht.setName("H" + (hCounter + 1));

								horizontalTiles.add(ht);

								hCounter++;
							}
						}
					}
				}
			}
		}

		for (int i = 0; i < 6; i++) {
			for (int j = 0; j < 6; j++) {
				if (grid[i][j] == null) {
					grid[i][j] = ". ";
				}
			}
		}

		maze.setMouse(mouse);
		maze.setVerticalTiles(verticalTiles);
		maze.setHorizontalTiles(horizontalTiles);
		maze.setGrid(grid);

		return maze;
	}

	/**
	 * Reads the strategy from the user and calls method MiM. Then prints
	 * whether there was a solution (what the path and its cost is) or not and
	 * the number of nodes expanded.
	 * 
	 * @param args
	 * @throws IOException
	 * 
	 * 
	 * 
	 */
	@SuppressWarnings("unchecked")
	public static void main(String[] args) throws IOException {
		//
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		System.out.println("Please specify which strategy to use");
		String strategy = br.readLine();
		// Maze maze = drawMaze();
		LinkedList<Object> output = new LinkedList<Object>();
		output = MiM(strategy, true);
		try {
			String s = (String) output.get(0);
			System.out.println(s);
			System.out.println("Path cost is  " + output.get(1));
			System.out.println("Number of nodes expanded is " + output.get(2));
		} catch (Exception e) {
			System.out.println("The solution is:- ");
			LinkedList<Node> path = new LinkedList<Node>();
			path = (LinkedList<Node>) output.get(0);
			for (Node node : path) {
				Maze maze = (Maze) node.getState();
				for (int i = 0; i < 6; i++) {
					for (int j = 0; j < 6; j++) {

						System.out.print(maze.getGrid()[j][i]);
					}
					System.out.println();
				}
				System.out.println();
				System.out.println();

			}
			System.out.println("Path cost is " + output.get(1));
			System.out.println("Number of nodes expanded is " + output.get(2));
		}

	}
}
